<template>
  <header
    class="h-14 w-full flex justify-end items-center border-b dark:border-b-2 dark:border-darker border-gray-200"
  >
    <ElButton
      type="primary"
      size="default"
      @click="toggleFullScreen"
      class="mr-5"
    >
      <ElIcon :size="12" color="#fff" class="mr-1">
        <FullScreen />
      </ElIcon>
      全屏
    </ElButton>
    <ElButton color="#683CFD" size="default" @click="onGenerate" class="mr-10">
      <ElIcon :size="size" color="#fff" class="mr-1">
        <Download />
      </ElIcon>
      合成视频
    </ElButton>
    <el-switch
      class="mr-10"
      size="large"
      :active-icon="Moon"
      :inactive-icon="Sunny"
      :inline-prompt="inner"
      v-model="store.isDark"
      :style="switchClass"
    />
  </header>
</template>

<script setup lang="ts">
import { ref, computed, toRaw } from "vue";
import { Download, Sunny, Moon, FullScreen } from "@element-plus/icons-vue";
import { usePageState } from "@/stores/pageState";
import { useTrackState } from "@/stores/trackState";
import { usePlayerState } from "@/stores/playerState";
import { AudioTrack } from "@/class/AudioTrack";
import type { Track } from "@/class/Track";
import { Combinator, type OffscreenSprite } from "@webav/av-cliper";
import { createFileWriter } from "@/utils/common";
import { ElLoading, ElMessage } from "element-plus";

const trackState = useTrackState();

const playerState = usePlayerState();

const store = usePageState();
const size = ref(14);
const inner = ref(true);
const switchClass = computed(() => ({
  "--el-switch-border-color": store.isDark ? "#4B5563" : "#D1D5DB",
  "--el-color-white": store.isDark ? "#F3F4F6" : "#374151",
}));

const onGenerate = async () => {
  try {
    // 先显示保存对话框获取用户确认
    const fileWriter = await createFileWriter();

    // 用户确认保存后，再显示加载状态并开始合成
    const loading = ElLoading.service({ text: "正在合成视频" });
    const start = performance.now();
    // 用于存储所有轨道的合成sprite Promise
    const sprs: Promise<OffscreenSprite>[] = [];

    // 先把所有轨道内容添加到sprs
    for (const track of trackState.trackList.reduce(
      (res, { list }) => res.concat(list as Track[]),
      [] as Track[]
    )) {
      if (track instanceof AudioTrack) {
        sprs.push(toRaw(track).combine());
      } else {
        sprs.push(
          toRaw(track).combine(
            {
              width: playerState.playerWidth,
              height: playerState.playerHeight,
            },
            playerState.PLAYER_SCALE
          )
        );
      }
    }
    // 等待所有sprite合成完成, Promise<OffscreenSprite>[] 变成 OffscreenSprite[]
    const sprites: OffscreenSprite[] = await Promise.all(sprs);

    /**
     * 合成视频, 导出width * height 分辨率的视频
     * @bitrate 720P 推荐 1.5Mbps， 1080P 推荐 3Mbps
     * @videoCodec 不要换成H.265或AV1编码，很多设备不支持，会导出失败
     * @bgColor 背景颜色，默认黑色
     */
    const combinator = new Combinator({
      width: playerState.playerWidth * playerState.PLAYER_SCALE,
      height: playerState.playerHeight * playerState.PLAYER_SCALE,
      bitrate: 1.5 * 1000 * 1000,
      videoCodec: "avc1.4D401F",
      bgColor: "black",
    });
    // 按z-index添加sprite，添加到Combinator中
    await Promise.all(
      sprites.map((sprite, index) => {
        sprite.zIndex = 999 - index;
        return combinator.addSprite(sprite);
      })
    );

    loading.setText(
      `视频合成完成，耗时: ${(performance.now() - start).toFixed(2)}ms`
    );

    // 预估视频大小
    const estimatedSizeMB = (
      (1.5 * 1000 * 1000 * (trackState.frameCount / 30)) /
      (1024 * 1024 * 8)
    ).toFixed(2);

    // 创建自定义的WritableStream来处理进度更新
    let totalBytesWritten = 0;
    // 上次更新进度条的时间
    let lastProgressTime = performance.now();
    const progressStream = new WritableStream({
      write(chunk) {
        totalBytesWritten += chunk.byteLength;

        // 每500ms更新一次进度
        const currentTime = performance.now();
        if (currentTime - lastProgressTime > 500) {
          const sizeMB = (totalBytesWritten / (1024 * 1024)).toFixed(2);
          lastProgressTime = currentTime;
          // 更新UI进度显示
          loading.setText(
            `正在导出视频 (${sizeMB}MB,  预计: ${estimatedSizeMB}MB)`
          );
        }

        // 写入到文件
        return fileWriter.write(chunk.buffer as ArrayBuffer);
      },

      close() {
        return fileWriter.close();
      },
    });

    // 使用pipeTo把Combinator的输出流导出到progressStream，progressStream 会处理进度更新和写入文件
    await combinator.output().pipeTo(progressStream);

    loading.close();

    const totalTime = performance.now() - start;
    const sizeMB = (totalBytesWritten / (1024 * 1024)).toFixed(2);
    ElMessage.success(
      `已保存本地, 耗时: ${(totalTime / 1000).toFixed(2)}s, 大小: ${sizeMB}MB`
    );

    console.log(
      `导出总耗时: ${(totalTime / 1000).toFixed(2)}s, 文件大小: ${sizeMB}MB`
    );
  } catch (error) {
    // 处理用户取消保存或其他错误
    if (
      error instanceof Error &&
      (error.name === "AbortError" ||
        error.message.includes("AbortError") ||
        error.message.includes("canceled") ||
        error.message.includes("aborted"))
    ) {
      // 用户取消了保存操作
      ElMessage.info("用户取消了保存操作");
    } else {
      // 其他错误情况
      console.error("合成视频失败:", error);
      ElMessage.error("合成视频失败");
    }
  }
};

const toggleFullScreen = () => {
  if (!document.fullscreenElement) {
    document.documentElement.requestFullscreen();
  } else {
    document.exitFullscreen();
  }
};
</script>
